#pragma once

#include <TinyXML/tinyxml.h>
#include <map>
#include <string>
#include <map>

#include "AssemblyTemplate.h"
#include "ComponentTemplate.h"

namespace ECS
{

	class IMemberValueFactory
	{
	public:
		virtual MemberValue* create() = 0;

		virtual void parseAttribute(MemberValue* value, const char* attr) = 0;

		virtual void parseElement(MemberValue* value, TiXmlElement* element) = 0;
	};

	class IXmlTemplateReader
	{
	public:
		virtual IComponentTemplate* readComponent(TiXmlElement* element) = 0;
	};


	class XMLComponentReader : public IXmlTemplateReader
	{
	public:
		template <typename TType, class TFactory>
		void registerType()
		{
			factories[getTypeID<TType>()] = new TFactory();
		}

		XMLComponentReader();

		virtual IComponentTemplate* readComponent(TiXmlElement* element) override;

		void readComponent(ComponentTemplate* ctemplate, TiXmlElement* element);

		MetaClass* classTable;
	protected:
		void readVariables(ComponentTemplate* ctemplate, MetaClass* classTable, TiXmlElement* element);

		void readConnections(ComponentTemplate* ctemplate, MetaClass* classTable, TiXmlElement* element);

		std::map<TypeID, IMemberValueFactory*> factories;
	};

	namespace impl
	{


		template <class TDef, class TComponent>
		class XmlTemplateReader : public IXmlTemplateReader
		{
		public:
			virtual IComponentTemplate* readComponent(TiXmlElement* element) override
			{
				auto cTemplate = new impl::ComponentTemplate<TDef, TComponent>();
				cTemplate->definition = new TDef();
				cTemplate->definition->initWith(element);

				return cTemplate;
			}
		};
	}

	class XMLReader
	{
	public:
		void readAssemblyDef(TiXmlElement* element, AssemblyTemplate* def);

		template <class TDef, class TComponent>
		void registerClass(const char* className);

		template <class TClass>
		void registerClass(const char* className);
	protected:
		typedef std::map < std::string, IXmlTemplateReader* > MapStringReader;
		MapStringReader _readers;
	};

	template <class TClass>
	void ECS::XMLReader::registerClass( const char* className )
	{
		auto reader = new XMLComponentReader();
		MetaClass* table = TClass::_metaTable();
		reader->classTable= table;
		_readers[std::string(className)] = reader;
	}

	template <class TDef, class TComponent>
	void ECS::XMLReader::registerClass( const char* className )
	{
		_readers[std::string(className)] = new impl::XmlTemplateReader<TDef, TComponent>();
	}

}